package com.divillysausages.dsair.input 
{
	import com.divillysausages.dsair.DSAir;
	import flash.display.FrameLabel;
	import flash.display.InteractiveObject;
	import flash.display.MovieClip;
	import flash.display.SimpleButton;
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.filters.ColorMatrixFilter;
	import flash.text.TextField;
	
	/**
	 * Generic button class to help with creating interactive buttons easily (if you don't
	 * want to use the PushButton class in minimal comps)
	 * @author Damian Connolly
	 */
	public class Button 
	{
		
		/********************************************************************/
		
		// the filter we use when the button is disabled and can't be clicked on
		private static const DISABLED_FILTER:ColorMatrixFilter = new ColorMatrixFilter( [0.212671, 0.71516, 0.072169, 0.0, 0.0, 0.212671, 0.71516, 0.072169, 0.0, 0.0, 0.212671, 0.71516, 0.072169, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7, 0.0] );
		
		private static const OVER_LABEL:String 	= "over";	// the label name for our over state
		private static const OUT_LABEL:String	= "out";	// the label name for our out state
		private static const DOWN_LABEL:String	= "down";	// the label name for our down state
		
		/********************************************************************/
		
		private var m_graphics:InteractiveObject 	= null; // the graphics for this button
		private var m_enabled:Boolean				= true;	// is the button enabled?
		private var m_hasOverLabel:Boolean			= false;// do our graphics have a frame for the over state?
		private var m_hasOutLabel:Boolean			= false;// do our graphics have a frame for the out state?
		private var m_hasDownLabel:Boolean			= false;// do our graphics have a frame for the down state?
		private var m_onOver:Function				= null;	// the callback for when we mouse over the button
		private var m_onOut:Function				= null;	// the callback for when we mouse out of the button
		private var m_onDown:Function				= null;	// the callback for when we mouse down on the button
		private var m_onUp:Function					= null;	// the callback for when we mouse up on the button
		private var m_onClick:Function				= null;	// the callback for when we click the button
		
		/********************************************************************/
		
		/**
		 * The x position of the button
		 */
		public function get x():Number { return ( this.m_graphics != null ) ? this.m_graphics.x : 0.0; }
		public function set x( n:Number ):void
		{
			if( this.m_graphics != null )
				this.m_graphics.x = n;
		}
		
		/**
		 * The y position of the button
		 */
		public function get y():Number { return ( this.m_graphics != null ) ? this.m_graphics.y : 0.0; }
		public function set y( n:Number ):void
		{
			if( this.m_graphics != null )
				this.m_graphics.y = n;
		}
		
		/**
		 * The width of the button
		 */
		public function get width():Number { return ( this.m_graphics != null ) ? this.m_graphics.width : 0.0; }
		public function set width( n:Number ):void
		{
			if( this.m_graphics != null )
				this.m_graphics.width = n;
		}
		
		/**
		 * The height of the button
		 */
		public function get height():Number { return ( this.m_graphics != null ) ? this.m_graphics.height : 0.0; }
		public function set height( n:Number ):void
		{
			if( this.m_graphics != null )
				this.m_graphics.height = n;
		}
		
		/**
		 * The graphics for the button
		 */
		public function get graphics():InteractiveObject { return this.m_graphics; }
		
		/**
		 * Is this button enabled
		 */
		public function get enabled():Boolean { return this.m_enabled; }
		public function set enabled( b:Boolean ):void
		{
			this.m_enabled = b;
			if ( this.m_enabled )
				this._activate();
			else
				this._deactivate();
		}
		
		/********************************************************************/
		
		/**
		 * Creates a new button
		 * @param graphics The graphics for this button
		 * @param onClick The callback to call when we click this button. It can take
		 * an optional parameter of type Button
		 */
		public function Button( graphics:InteractiveObject, onClick:Function = null ) 
		{
			this.m_graphics = graphics;
			
			// set our mouse properties
			var s:Sprite = this.m_graphics as Sprite;
			if ( s != null )
				s.mouseChildren = false;
				
			// if the graphics are a textfield
			var t:TextField = this.m_graphics as TextField;
			if ( t != null )
			{
				t.selectable 		= false;
				t.mouseWheelEnabled	= false;
			}
			
			// the the graphics are a movieclip
			var mc:MovieClip = this.m_graphics as MovieClip;
			if ( mc != null )
				this._checkForLabels();
			
			// set our callbacks
			this.addEventCallbacks( onClick );
		}
		
		/**
		 * Destroys the button and clears it for garbage collection
		 * @param destroyGraphics Should we also destroy the graphics?
		 */
		public function destroy( destroyGraphics:Boolean = false ):void
		{
			// deactivate
			this.enabled = false;
			
			// remove our graphics if needed
			if ( destroyGraphics && this.m_graphics != null && this.m_graphics.parent != null )
				this.m_graphics.parent.removeChild( this.m_graphics );
				
			// null our props
			this.m_graphics	= null;
			this.m_onOver	= null;
			this.m_onOut	= null;
			this.m_onDown	= null;
			this.m_onUp		= null;
			this.m_onClick	= null;
		}
		
		/**
		 * Adds the event callbacks for the buttons. All callbacks take can take one optional
		 * parameter of type Button
		 * @param onClick Called when we click the button
		 * @param onOver Called when we mouse over the button
		 * @param onOut Called when we mouse out of the button
		 * @param onDown Called when we mouse down on the button
		 * @param onUp Called when we mouse up on the button
		 */
		public function addEventCallbacks( onClick:Function = null, onOver:Function = null, onOut:Function = null, onDown:Function = null, onUp:Function = null ):void
		{
			// remove any previous callbacks
			var wasEnabled:Boolean 	= this.enabled;
			this.enabled			= false;
			
			// check our callbacks
			if ( onClick != null && onClick.length > 1 )
			{
				DSAir.error( this, "The onClick callback can take one optional parameter of type button", false );
				onClick = null;
			}
			if ( onOver != null && onOver.length > 1 )
			{
				DSAir.error( this, "The onOver callback can take one optional parameter of type button", false );
				onOver = null;
			}
			if ( onOut != null && onOut.length > 1 )
			{
				DSAir.error( this, "The onOut callback can take one optional parameter of type button", false );
				onOut = null;
			}
			if ( onDown != null && onDown.length > 1 )
			{
				DSAir.error( this, "The onDown callback can take one optional parameter of type button", false );
				onDown = null;
			}
			if ( onUp != null && onUp.length > 1 )
			{
				DSAir.error( this, "The onUp callback can take one optional parameter of type button", false );
				onUp = null;
			}
			
			// store them
			this.m_onClick	= onClick;
			this.m_onOver	= onOver;
			this.m_onOut	= onOut;
			this.m_onDown	= onDown;
			this.m_onUp		= onUp;
			
			// activate
			if( wasEnabled )
				this.enabled = true;
		}
		
		/********************************************************************/
		
		// checks for our frame labels (if our graphics are a MovieClip)
		private function _checkForLabels():void
		{
			// check through our frames and see do we have any for the over, out and down states
			var frames:Array = ( this.m_graphics as MovieClip ).currentLabels;
			for each( var frame:FrameLabel in frames )
			{
				if ( frame.name == Button.OVER_LABEL )
					this.m_hasOverLabel = true;
				if ( frame.name == Button.OUT_LABEL )
					this.m_hasOutLabel = true;
				if ( frame.name == Button.DOWN_LABEL )
					this.m_hasDownLabel = true;
			}
			
			// if we have an out label, stop there
			if ( this.m_hasOutLabel )
				( this.m_graphics as MovieClip ).gotoAndStop( Button.OUT_LABEL );
		}
		
		// activates the button
		private function _activate():void
		{
			if ( this.m_graphics == null )
				return;
				
			// add our event listeners
			if( this.m_onOver != null )	this.m_graphics.addEventListener( MouseEvent.MOUSE_OVER, this._onOver );
			if( this.m_onOut != null )	this.m_graphics.addEventListener( MouseEvent.MOUSE_OUT, this._onOut );
			if( this.m_onDown != null )	this.m_graphics.addEventListener( MouseEvent.MOUSE_DOWN, this._onDown );
			if( this.m_onUp != null )	this.m_graphics.addEventListener( MouseEvent.MOUSE_UP, this._onUp );
			if( this.m_onClick != null )this.m_graphics.addEventListener( MouseEvent.CLICK, this._onClick );
			
			// set mouse properties
			this.m_graphics.mouseEnabled = true;
			if ( this.m_graphics is Sprite )
				( this.m_graphics as Sprite ).buttonMode = true;
			if ( this.m_graphics is SimpleButton )
				( this.m_graphics as SimpleButton ).enabled = true;
				
			// clear any filters
			this.m_graphics.filters = [];
		}
		
		// deactivates the button
		private function _deactivate():void
		{
			if ( this.m_graphics == null )
				return;
				
			// remove our event listeners
			this.m_graphics.removeEventListener( MouseEvent.MOUSE_OVER, this._onOver );
			this.m_graphics.removeEventListener( MouseEvent.MOUSE_OUT, this._onOut );
			this.m_graphics.removeEventListener( MouseEvent.MOUSE_DOWN, this._onDown );
			this.m_graphics.removeEventListener( MouseEvent.MOUSE_UP, this._onUp );
			this.m_graphics.removeEventListener( MouseEvent.CLICK, this._onClick );
			
			// set mouse properties
			this.m_graphics.mouseEnabled = false;
			if ( this.m_graphics is Sprite )
				( this.m_graphics as Sprite ).buttonMode = false;
			if ( this.m_graphics is SimpleButton )
				( this.m_graphics as SimpleButton ).enabled = false;
				
			// add our disabled filter
			this.m_graphics.filters = [Button.DISABLED_FILTER];
		}
		
		// called when we mouse over the button
		private function _onOver( e:MouseEvent ):void
		{			
			// if we have an over label (and our graphics are a MovieClip), play it
			if ( this.m_hasOverLabel )
				( this.m_graphics as MovieClip ).gotoAndStop( Button.OVER_LABEL );
				
			// no callback, return
			if ( this.m_onOver == null )
				return;
			
			// call our callback
			if ( this.m_onOver.length == 0 )
				this.m_onOver();
			else
				this.m_onOver( this );
		}
		
		// called when we mouse out of the button
		private function _onOut( e:MouseEvent ):void
		{			
			// if we have an out label (and our graphics are a MovieClip), play it
			if ( this.m_hasOutLabel )
				( this.m_graphics as MovieClip ).gotoAndStop( Button.OUT_LABEL );
				
			// no callback, return
			if ( this.m_onOut == null )
				return;
			
			// call our callback
			if ( this.m_onOut.length == 0 )
				this.m_onOut();
			else
				this.m_onOut( this );
		}
		
		// called when we mouse down the button
		private function _onDown( e:MouseEvent ):void
		{			
			// if we have a down label (and our graphics are a MovieClip), play it
			if ( this.m_hasDownLabel )
				( this.m_graphics as MovieClip ).gotoAndStop( Button.DOWN_LABEL );
				
			// no callback, return
			if ( this.m_onDown == null )
				return;
			
			// call our callback
			if ( this.m_onDown.length == 0 )
				this.m_onDown();
			else
				this.m_onDown( this );
		}
		
		// called when we mouse up on the button
		private function _onUp( e:MouseEvent ):void
		{			
			// if we have an over label (and our graphics are a MovieClip), play it
			if ( this.m_hasOverLabel )
				( this.m_graphics as MovieClip ).gotoAndStop( Button.OVER_LABEL );
				
			// no callback, return
			if ( this.m_onUp == null )
				return;
			
			// call our callback
			if ( this.m_onUp.length == 0 )
				this.m_onUp();
			else
				this.m_onUp( this );
		}
		
		// called when we click the button
		private function _onClick( e:MouseEvent ):void
		{			
			// no callback, return
			if ( this.m_onClick == null )
				return;
			
			// call our callback
			if ( this.m_onClick.length == 0 )
				this.m_onClick();
			else
				this.m_onClick( this );
		}
		
	}

}